#include "laurin-window.h"
#include "laurin-scan-result-model.h"
#include "../core/laurin-utils.h"
#include <gtkmm/dialog.h>
#include <gtkmm/button.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/combobox.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/scale.h>
#include <gtkmm/entry.h>
#include <gtkmm/actiongroup.h>
#include <gtkmm/action.h>
#include <gtkmm/stock.h>
#include <gtkmm/statusbar.h>
#include <gtkmm/box.h>
#include <gtkmm/frame.h>
#include <gtkmm/aboutdialog.h>
#include <gtkmm/treemodelcolumn.h>
#include <gtkmm/treemodel.h>
#include <gtkmm/treeview.h>
#include <gtkmm/liststore.h>



#define LAURIN_UI_FILE			"/mnt/Data/Workspace/laurin/laurin/share/laurin-ui.ui"
#define LAURIN_ICON_FILE		"/mnt/Data/Workspace/laurin/laurin/share/laurin-icon.png"
#define LAURIN_ACTIONS_FILE		"/mnt/Data/Workspace/laurin/laurin/share/menubar.xml"



LaurinWindow::~LaurinWindow()
{
	if(_scan_model)
		delete _scan_model;

	delete _controller;
}


bool LaurinWindow::initialize(int argc, char** argv)
{
	if(!Glib::thread_supported())
		Glib::thread_init();


	_app = NULL;
	_scan_model = NULL;
	_stop_request = false;
	_kit = new Gtk::Main(argc, argv);

	_builder = Gtk::Builder::create_from_file(LAURIN_UI_FILE);
	_builder->get_widget("laurin_main_window", _app);

	_app->set_icon_from_file(LAURIN_ICON_FILE);


	_controller = new LaurinController();

	return true;
}


void LaurinWindow::run()
{

	setActions();
	setRunningMode(false);

	setStatus("Laurin is ready!");
	_kit->run(*_app);
}


void LaurinWindow::setActions()
{

	Glib::RefPtr<Gtk::ActionGroup> actions = Gtk::ActionGroup::create();


	// FILE MENU
	actions->add(Gtk::Action::create("MenuFile", "_File"));
	actions->add(Gtk::Action::create("Run", Gtk::Stock::CONNECT), sigc::mem_fun(*this, &LaurinWindow::on_action_file_run));
	actions->add(Gtk::Action::create("Stop", Gtk::Stock::DISCONNECT), sigc::mem_fun(*this, &LaurinWindow::on_action_file_stop));
	actions->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &LaurinWindow::on_action_file_quit));

	// EDIT MENU
	actions->add(Gtk::Action::create("MenuEdit", "_Edit"));
	actions->add(Gtk::Action::create("Preferences", Gtk::Stock::PREFERENCES), sigc::mem_fun(*this, &LaurinWindow::on_action_edit_preferences));

	// HELP MENU
	actions->add(Gtk::Action::create("MenuHelp", "_Help"));
	actions->add(Gtk::Action::create("About", Gtk::Stock::ABOUT), sigc::mem_fun(*this, &LaurinWindow::on_action_help_about));


	_ui_manager = Gtk::UIManager::create();
	_ui_manager->insert_action_group(actions);
	_ui_manager->add_ui_from_file(LAURIN_ACTIONS_FILE);


	// MENU BAR

	Gtk::VBox* menubarc = NULL;
	_builder->get_widget("laurin_menubar_container", menubarc);

	menubarc->pack_start(*(_ui_manager->get_widget("/MenuBar")), false, true, 0);


	// TOOL BAR

	Gtk::VBox* toolbarc = NULL;
	_builder->get_widget("laurin_toolbar_container", toolbarc);

	toolbarc->pack_start(*(_ui_manager->get_widget("/ToolBar")), false, true, 0);


	// EDIT BUTTON

	Gtk::Button* button = NULL;
	_builder->get_widget("laurin_ap_distance_button", button);
	button->signal_clicked().connect(sigc::mem_fun(this, &LaurinWindow::on_edit_ap_distance_click));
	button->hide();
}


void LaurinWindow::setRunningMode(bool flag)
{
	_ui_manager->get_action("/ui/MenuBar/MenuFile/Run")->set_sensitive(!flag);
	_ui_manager->get_action("/ui/MenuBar/MenuFile/Stop")->set_sensitive(flag);
	_ui_manager->get_action("/ui/MenuBar/MenuEdit/Preferences")->set_sensitive(!flag);

	setProgressBar(flag);
}


void LaurinWindow::setStatus(Glib::ustring statusMessage)
{
	Gtk::Statusbar* statusbar = NULL;
	_builder->get_widget("laurin_main_statusbar", statusbar);
	statusbar->push(statusMessage);
}


void LaurinWindow::removeStatus()
{
	Gtk::Statusbar* statusbar = NULL;
	_builder->get_widget("laurin_main_statusbar", statusbar);
	statusbar->pop();
}


void LaurinWindow::setProgressBar(bool flag)
{
	_builder->get_widget("laurin_main_progressbar", _progress_bar);
	_progress_bar->set_sensitive(flag);

	if(flag)
	{
		_progress_bar->show();
		_progress_bar_timer = Glib::signal_timeout().connect(sigc::mem_fun(*this, &LaurinWindow::on_progress_bar_tick), 100);
	}
	else
	{
		_progress_bar->hide();
		_progress_bar_timer.disconnect();
		_progress_bar->set_fraction(0.0);
	}
}


void LaurinWindow::clearResult()
{
	_app->set_sensitive(false);

	Gtk::TreeView* resultview = NULL;
	_builder->get_widget("laurin_main_tree", resultview);

	if(_scan_model)
		_scan_model->clear();
	else
		_scan_model = new LaurinScanResultModel();

	resultview->set_model(_scan_model->getStore());


	Gtk::Button* button = NULL;
	_builder->get_widget("laurin_ap_distance_button", button);
	button->hide();

	_app->set_sensitive(true);
}


void LaurinWindow::fillResult(bool error)
{

	_app->set_sensitive(false);

	removeStatus();
	setRunningMode(false);

	if(error)
	{
		Gtk::MessageDialog(*_app, Glib::ustring("Error on occurred:\n").append(_controller->getErrorMessage()), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true).run();
		clearResult();
		setStatus(Glib::ustring("An error is raised: ").append(_controller->getErrorMessage()));
	}
	else
	{
		if(_controller->getOptions().EnableLocation)
		{
			Gtk::Button* button = NULL;
			_builder->get_widget("laurin_ap_distance_button", button);
			button->show();
		}


		Gtk::TreeView* resultview = NULL;
		_builder->get_widget("laurin_main_tree", resultview);

		if(_scan_model)
			delete _scan_model;

		_scan_model = _controller->getScanResultModel();

		resultview->set_model(_scan_model->getStore());


		if(resultview->get_columns().size() == 0)
		{
			resultview->get_selection()->set_mode(Gtk::SELECTION_SINGLE);
			resultview->append_column("Number", _scan_model->record.sequence);
			resultview->append_column("SSID", _scan_model->record.ssid);
			resultview->append_column("Associated AP", _scan_model->record.bssid);
			resultview->append_column("MAC Address", _scan_model->record.mac);
			resultview->append_column("IP Address", _scan_model->record.ip);
			resultview->append_column("Distance", _scan_model->record.distance);
			resultview->append_column("Station Type", _scan_model->record.type);
		}

		setStatus("Operation is complete!");
	}

	_app->set_sensitive(true);
}


void LaurinWindow::on_action_file_run()
{
	_stop_request = false;
	if(_controller->start())
	{
		clearResult();
		setRunningMode(true);
		setStatus("Laurin is now discovering...");
	}
	else
	{
		Gtk::MessageDialog(*_app, Glib::ustring("Error on start modules:\n").append(_controller->getErrorMessage()), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true).run();
	}
}


void LaurinWindow::on_action_file_stop()
{
	_stop_request = true;
	if(_controller->stop())
	{
		clearResult();
		setRunningMode(false);
		setStatus("Operation stopped by user");
	}
	else
	{
		Gtk::MessageDialog(*_app, Glib::ustring("Error on stop modules:\n").append(_controller->getErrorMessage()), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true).run();
	}
}


void LaurinWindow::on_action_file_quit()
{
	if(_controller->isRunning())
	{
		if(Gtk::MessageDialog(*_app, "Laurin is searching. Do you want to quit?!", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true).run() == Gtk::RESPONSE_NO)
			return;

		_controller->stop(true);
	}

	_kit->quit();
}


void LaurinWindow::on_action_help_about()
{
	Gtk::AboutDialog* dialog = NULL;
	_builder->get_widget("laurin_about_dialog", dialog);

	dialog->set_icon_from_file(LAURIN_ICON_FILE);
	dialog->set_logo(Gdk::Pixbuf::create_from_file(LAURIN_ICON_FILE));
	dialog->signal_response().connect(sigc::mem_fun(*this, &LaurinWindow::on_about_dialog_response));
	dialog->run();
}


void LaurinWindow::on_about_dialog_response(int response)
{
	Gtk::AboutDialog* dialog = NULL;
	_builder->get_widget("laurin_about_dialog", dialog);
	dialog->hide();
}


void LaurinWindow::on_action_edit_preferences()
{
	Gtk::Dialog* preferences = NULL;
	_builder->get_widget("laurin_preferences_dialog", preferences);

	//preferences->signal_response().connect(sigc::mem_fun(*this, &LaurinWindow::on_preferences_dialog_response));

	preferences->set_icon_from_file(LAURIN_ICON_FILE);


	// old options
	LaurinControllerOptions options = _controller->getOptions();

	// get devices
	std::list<Glib::ustring>* aux = getAvaileDeviceForLaurin();

	if(aux->size() == 0)
	{
		Gtk::MessageDialog(*_app, "There aren't wireless devices compatible with laurin!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true).run();
		return;
	}


	Gtk::TreeModelColumn<Glib::ustring> model;			// show only the name for every devices
	Gtk::TreeModel::ColumnRecord record;
	record.add(model);
	Glib::RefPtr<Gtk::ListStore> devstore = Gtk::ListStore::create(record);

	std::list<Glib::ustring>::iterator b = aux->begin();
	std::list<Glib::ustring>::iterator e = aux->end();

	// save the position of current device's names
	Gtk::TreeModel::iterator nd;		// network device
	Gtk::TreeModel::iterator md;		// monitor device

	while(b != e)
	{
		Gtk::TreeModel::iterator iter =  devstore->prepend();
		Gtk::TreeModel::Row row = *iter;

		row.set_value(0, *b);

		if(options.NetworkDeviceName == *b)
		{
			nd = iter;
		}

		if(options.MasterDeviceName == *b)
		{
			md = iter;
		}

		b++;
	}


	delete aux;


	 Gtk::ComboBox* ndc = NULL;
	 _builder->get_widget("laurin_network_device_combobox", ndc);

	 ndc->clear();
	 ndc->set_model(devstore);
	 ndc->pack_start(model);

	 if(nd)
		 ndc->set_active(nd);
	 else
		 ndc->set_active(0);


	 Gtk::ComboBox* mdc = NULL;
	 _builder->get_widget("laurin_master_device_combobox", mdc);

	 mdc->clear();
	 mdc->set_model(devstore);
	 mdc->pack_start(model);

	 if(md)
		 mdc->set_active(md);
	 else
		 mdc->set_active(0);


	 Gtk::Entry* mde = NULL;
	 _builder->get_widget("laurin_monitor_device_entry", mde);

	 mde->set_text(options.MonitorDeviceName);


	 Gtk::HScale* nts = NULL;
	 _builder->get_widget("laurin_network_timeout_scale", nts);
	 nts->set_value(options.NetworkTimeout);



	 Gtk::HScale* cfs = NULL;
	 _builder->get_widget("laurin_captured_frame_scale", cfs);
	 cfs->set_value(options.MonitorMaxCapturedFrame);


	 Gtk::HScale* cis = NULL;
	 _builder->get_widget("laurin_capture_interval_scale", cis);
	 cis->set_value(options.MonitorCaptureInterval);


	 Gtk::CheckButton* el = NULL;
	 _builder->get_widget("laurin_location_check", el);
	 el->set_active(options.EnableLocation);


//	 _builder->get_widget("laurin_passive_check", el);
//	 el->set_active(options.PassiveMode);
//	 el->signal_toggled().connect(sigc::mem_fun(*this, &LaurinWindow::on_preferences_passive_mode_toogled));

	 int a = preferences->run();
	 fprintf(stderr, "%d\n", a);
}


void LaurinWindow::on_preferences_dialog_response(int response)
{
	Gtk::Dialog* preferences = NULL;
	_builder->get_widget("laurin_preferences_dialog", preferences);


	if(response == Gtk::RESPONSE_APPLY)
	{
		LaurinControllerOptions options;

		Gtk::ComboBox* combo = NULL;
		_builder->get_widget("laurin_network_device_combobox", combo);

		Gtk::TreeModel::iterator iter =  combo->get_active();
		if(iter)
		{
			Gtk::TreeModel::Row row = *iter;
			row.get_value(0, options.NetworkDeviceName);
		}

		_builder->get_widget("laurin_master_device_combobox", combo);
		iter = combo->get_active();
		if(iter)
		{
			Gtk::TreeModel::Row row = *iter;
			row.get_value(0, options.MasterDeviceName);
		}

		Gtk::Entry* entry = NULL;
		_builder->get_widget("laurin_monitor_device_entry", entry);

		options.MonitorDeviceName = entry->get_text();


		Gtk::HScale* scale = NULL;
		_builder->get_widget("laurin_capture_interval_scale", scale);
		options.MonitorCaptureInterval = (int) scale->get_value();


		_builder->get_widget("laurin_captured_frame_scale", scale);
		options.MonitorMaxCapturedFrame = (int) scale->get_value();

		_builder->get_widget("laurin_network_timeout_scale", scale);
		options.NetworkTimeout = (int) scale->get_value();

		Gtk::CheckButton* check = NULL;
		_builder->get_widget("laurin_location_check", check);

		options.EnableLocation = check->get_active();


		if(!options.areValid())
		{
			Gtk::MessageDialog(*_app, Glib::ustring("Preferences are not valid:\n").append(options.getErrorMessage()), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true).run();
		}
		else
		{
			preferences->hide();

			if(!_controller->setOptions(options))
			{
				Gtk::MessageDialog(*_app, Glib::ustring("Preferences are saved because:\n").append(_controller->getErrorMessage()), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true).run();
			}
		}
	}
	else
		preferences->hide();
}


void LaurinWindow::on_edit_ap_distance_click()
{
	Gtk::Dialog* edit = NULL;
	_builder->get_widget("laurin_edit_distance_dialog", edit);

	edit->signal_response().connect(sigc::mem_fun(this, &LaurinWindow::on_edit_ap_response));

	Gtk::TreeView* distance = NULL;
	_builder->get_widget("laurin_distance_tree", distance);



	distance->set_model(_scan_model->getAccessPointModel()->getStore());

	if(distance->get_columns().size() == 0)
	{
		distance->get_selection()->set_mode(Gtk::SELECTION_SINGLE);
		distance->append_column("SSID", _scan_model->getAccessPointModel()->record.ssid);
		distance->append_column("BSSID", _scan_model->getAccessPointModel()->record.bssid);
		distance->append_column_editable("Distance", _scan_model->getAccessPointModel()->record.distance);
	}

	edit->run();
}


void LaurinWindow::on_edit_ap_response(int response)
{
	Gtk::Dialog* edit = NULL;
	_builder->get_widget("laurin_edit_distance_dialog", edit);

	if(response == Gtk::RESPONSE_APPLY)
	{

		Glib::RefPtr<Gtk::ListStore> store = _scan_model->getAccessPointModel()->getStore();

		Gtk::TreeModel::iterator b = store->children().begin();
		Gtk::TreeModel::iterator e = store->children().end();

		bool somechange = false;

		while(b != e)
		{
			if((*b)[_scan_model->getAccessPointModel()->record.distance] > 0)
			{
				_controller->setDistanceBetweenMonitorAndAP((*b)[_scan_model->getAccessPointModel()->record.bssid], (*b)[_scan_model->getAccessPointModel()->record.distance]);
				somechange = true;
			}

			b++;
		}

		if(somechange)
			fillResult(false);
	}

	edit->hide();
}


void LaurinWindow::on_preferences_passive_mode_toogled()
{
	Gtk::CheckButton* el = NULL;
	_builder->get_widget("laurin_passive_check", el);

	Gtk::ComboBox* box = NULL;
	_builder->get_widget("laurin_network_device_combobox", box);

	Gtk::HScale* scale = NULL;
	_builder->get_widget("laurin_network_timeout_scale", scale);

	box->set_sensitive(!el->get_active());
	scale->set_sensitive(!el->get_active());
}


bool LaurinWindow::on_progress_bar_tick()
{
	_progress_bar->pulse();
	if(!_controller->isRunning())
	{
		if(!_stop_request)
			fillResult(_controller->isErrorRaised());

		return false;
	}

	return true;
}
